package net.neptech.service.impl;

import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.OverAllStateFactory;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.neptech.finalClass.SessionConstants;
import net.neptech.model.ipc.InteractionRecord;
import net.neptech.service.RedisService;
import net.neptech.service.SessionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author HuangZhengmin
 * @desc  会话服务实现类
 * @Date 2025/8/19 20:32
 * @Version 1.0
 */
@Service
public class SessionServiceImpl implements SessionService {

  @Autowired
  private RedisService redisService;

  @Autowired
  private StateGraph medicalWorkflowGraph;

  @Autowired
  private OverAllStateFactory stateFactory;

  @Autowired
  private ObjectMapper objectMapper;

  @Override
  public String createSession(Long patientId) {
    // 生成唯一会话ID
    String sessionId = UUID.randomUUID().toString();
    String metadataKey = SessionConstants.SESSION_METADATA_PREFIX + sessionId;
    String stateKey = SessionConstants.SESSION_STATE_PREFIX + sessionId;

    // 初始化会话元数据（Hash结构）
    Map<String, Object> metadata = new HashMap<>();
    metadata.put("patientId", patientId);
    metadata.put("status", "ACTIVE");
    metadata.put("currentNode", StateGraph.START);
    metadata.put("createdAt", LocalDateTime.now().toString());
    metadata.put("updatedAt", LocalDateTime.now().toString());
    redisService.setCacheMap(metadataKey, metadata);
    redisService.expire(metadataKey, SessionConstants.SESSION_EXPIRE_SECONDS, TimeUnit.SECONDS);

    // 初始化会话状态（OverAllState）
    OverAllState initialState = stateFactory.create();
    redisService.setCacheObject(stateKey, initialState, SessionConstants.SESSION_EXPIRE_SECONDS, TimeUnit.SECONDS);

    // 关联患者与会话
    String patientSessionsKey = SessionConstants.PATIENT_SESSIONS_PREFIX + patientId;
    Set<String> patientSessions = redisService.getCacheSet(patientSessionsKey);
    if (patientSessions == null) {
      patientSessions = new HashSet<>();
    }
    patientSessions.add(sessionId);
    redisService.setCacheSet(patientSessionsKey, patientSessions);
    redisService.expire(patientSessionsKey, SessionConstants.SESSION_EXPIRE_SECONDS, TimeUnit.SECONDS);

    return sessionId;
  }

  @Override
  public Map<String, Object> getSession(String sessionId) {
    String metadataKey = SessionConstants.SESSION_METADATA_PREFIX + sessionId;
    Map<String, Object> metadata = redisService.getCacheMap(metadataKey);
    if (metadata == null || metadata.isEmpty()) {
      throw new IllegalArgumentException("会话不存在: " + sessionId);
    }
    return metadata;
  }

/**
 * 保存会话状态的方法
 * @param sessionId 会话ID，用于唯一标识一个会话
 * @param state 需要保存的会话状态对象，包含会话的所有相关信息
 */
  @Override
  public void saveSessionState(String sessionId, OverAllState state) {
    // 构建Redis中的状态键，添加特定前缀以确保键的规范性和可识别性
    String stateKey = SessionConstants.SESSION_STATE_PREFIX + sessionId;
    // 将会话状态保存到Redis中，设置过期时间
    redisService.setCacheObject(stateKey, state, SessionConstants.SESSION_EXPIRE_SECONDS, TimeUnit.SECONDS);
  }

  @Override
  /**
   * 根据会话ID获取会话状态
   * @param sessionId 会话ID
   * @return OverAllState 会话状态对象，如果不存在则创建一个新的状态对象
   */
  public OverAllState getSessionState(String sessionId) {
    // 构建Redis中存储的状态键值，添加特定前缀
    String stateKey = SessionConstants.SESSION_STATE_PREFIX + sessionId;
    // 从Redis缓存中获取状态对象
    OverAllState state = redisService.getCacheObject(stateKey);
    // 如果状态对象存在则返回，否则创建一个新的状态对象返回
    return state != null ? state : stateFactory.create();
  }

  @Override
  /**
   * 记录用户与系统的交互信息
   * @param sessionId 会话ID，用于标识特定的用户会话
   * @param userInput 用户输入的内容
   * @param systemResponse 系统返回的响应内容
   * @param nodeName 当前交互的节点名称
   */
  public void recordInteraction(String sessionId, String userInput, String systemResponse, String nodeName) {
    try {
      // 构建Redis中存储交互记录的键名
      String recordsKey = SessionConstants.SESSION_RECORDS_PREFIX + sessionId;
      // 创建交互记录对象，包含用户输入、系统响应、节点名和当前时间
      InteractionRecord record = new InteractionRecord(userInput,systemResponse,nodeName,LocalDateTime.now());

      // 将记录序列化为JSON存储
      String recordJson = objectMapper.writeValueAsString(record);

//      // 使用 RedisService 的列表操作
//      List<String> currentRecords = redisService.getCacheList(recordsKey);
//      if (currentRecords == null) {
//        currentRecords = new ArrayList<>();
//      }
//      currentRecords.add(0, recordJson); // 添加到开头（最新记录在前面）
//      redisService.setCacheList(recordsKey, currentRecords);
      // 在列表头部添加单个记录（最新的在前面）
      redisService.leftPushToList(recordsKey, recordJson);
      redisService.expire(recordsKey, SessionConstants.SESSION_EXPIRE_SECONDS, TimeUnit.SECONDS);
    } catch (JsonProcessingException e) {
      throw new RuntimeException("记录交互失败", e);
    }
  }

/**
 * 获取指定会话ID的交互历史记录
 * @param sessionId 会话ID，用于标识唯一的会话
 * @return 返回该会话的所有交互记录列表，按时间顺序排列（最新的在前面）
 */
  @Override
  public List<InteractionRecord> getInteractionHistory(String sessionId) {
    // 构建Redis中存储记录的键，使用会话前缀和会话ID组合
    String recordsKey = SessionConstants.SESSION_RECORDS_PREFIX + sessionId;
    // 从Redis中获取存储的记录JSON字符串列表
    List<String> recordJsons = redisService.getCacheList(recordsKey);

    // 创建用于存储解析后的交互记录的列表
    List<InteractionRecord> records = new ArrayList<>();
    // 检查Redis中是否存在记录且列表不为空
    if (recordJsons != null && !recordJsons.isEmpty()) {
      try {
        // 遍历JSON字符串列表，将每个JSON字符串转换为InteractionRecord对象
        // 记录已经按时间顺序存储（最新的在前面）
        for (String json : recordJsons) {
          // 使用ObjectMapper将JSON字符串转换为InteractionRecord对象
          InteractionRecord record = objectMapper.readValue(json, InteractionRecord.class);
          // 将解析后的记录添加到结果列表中
          records.add(record);
        }
      } catch (Exception e) {
        // 如果解析过程中发生异常，抛出运行时异常
        throw new RuntimeException("获取交互历史失败", e);
      }
    }
    // 返回解析后的交互记录列表
    return records;
  }

  @Override
  public void updateSessionNode(String sessionId, String nodeName) {
    String metadataKey = SessionConstants.SESSION_METADATA_PREFIX + sessionId;
    redisService.setCacheMapValue(metadataKey, "currentNode", nodeName);
    redisService.setCacheMapValue(metadataKey, "updatedAt", LocalDateTime.now().toString());
    // 刷新过期时间
    redisService.expire(metadataKey, SessionConstants.SESSION_EXPIRE_SECONDS, TimeUnit.SECONDS);
  }

/**
 * 完成指定会话的方法
 * @param sessionId 需要完成的会话ID
 */
  @Override
  public void completeSession(String sessionId) {
    // 构建会话元数据的键，使用会话ID前缀
    String metadataKey = SessionConstants.SESSION_METADATA_PREFIX + sessionId;
    // 在Redis中设置会话状态为已完成
    redisService.setCacheMapValue(metadataKey, "status", "COMPLETED");
    // 更新会话的最后修改时间
    redisService.setCacheMapValue(metadataKey, "updatedAt", LocalDateTime.now().toString());
  }

/**
 * 处理用户输入并返回响应结果
 * @param sessionId 会话ID，用于标识当前用户会话
 * @param patientId 患者ID，用于关联患者信息
 * @param input 用户输入的内容
 * @return 包含处理结果的Map，包括响应内容、会话状态等信息
 */
  @Override
  public Map<String, Object> processUserInput(String sessionId, Long patientId, String input) {
    // 会话不存在则创建
    if (sessionId == null || sessionId.isEmpty() ||
            !redisService.hasKey(SessionConstants.SESSION_METADATA_PREFIX + sessionId)) {
      sessionId = createSession(patientId);
    }

    // 获取当前会话元数据和状态
    Map<String, Object> session = getSession(sessionId);
    String currentNode = session.get("currentNode").toString();
    OverAllState state = getSessionState(sessionId);
    setStateInput(state, input);

    Map<String, Object> result = new HashMap<>();
    try {
      // 编译图
      CompiledGraph compiledGraph = medicalWorkflowGraph.compile();

      // 准备输入
      Map<String, Object> inputs = new HashMap<>();
      inputs.put("input", input);
      inputs.put("currentNode", currentNode);

      // 执行图
      Optional<OverAllState> resultState = compiledGraph.invoke(inputs);

      if (resultState.isPresent()) {
        OverAllState newState = resultState.get();

        // 从状态中获取下一个节点
        String nextNode = (String) newState.value("nextNode")
                .orElse(StateGraph.END); // 假设状态中包含下一个节点信息

        // 更新会话状态和元数据
        saveSessionState(sessionId, newState);
        updateSessionNode(sessionId, nextNode);

        // 记录交互
        String response = (String) newState.value("final_recommendation")
                .orElse((String) newState.value("additional_question_text")
                        .orElse((String) newState.value("non_consult_response")
                                .orElse("正在处理您的请求，请稍候...")));
        recordInteraction(sessionId, input, response, currentNode);

        // 准备返回结果
        result.put("sessionId", sessionId);
        result.put("response", response);
        result.put("completed", StateGraph.END.equals(nextNode));

        // 结束会话
        if (StateGraph.END.equals(nextNode)) {
          completeSession(sessionId);
        }
      } else {
        result.put("error", "图执行未返回结果");
        result.put("sessionId", sessionId);
      }

    } catch (GraphStateException e) {
      result.put("error", "处理请求时发生错误: " + e.getMessage());
      result.put("sessionId", sessionId);
    } catch (Exception e) {
      result.put("error", "系统内部错误: " + e.getMessage());
      result.put("sessionId", sessionId);
    }

    return result;
  }

  @Override
/**
 * 获取指定患者的所有会话ID列表
 * @param patientId 患者ID，用于标识特定的患者
 * @return 返回患者所有会话ID的列表，如果没有会话则返回空列表
 */
  public List<String> getPatientSessions(Long patientId) {
    // 构建Redis中存储患者会话的键，使用患者ID作为后缀
    String key = SessionConstants.PATIENT_SESSIONS_PREFIX + patientId;
    // 从Redis中获取存储的会话ID集合
    Set<String> sessionIds = redisService.getCacheSet(key);

    // 检查会话ID集合是否为空或不存在
    if (sessionIds == null || sessionIds.isEmpty()) {
        // 如果没有会话，返回空列表
      return Collections.emptyList();
    }

    // 将Set转换为ArrayList并返回
    return new ArrayList<>(sessionIds);
  }

/**
 * 设置状态输入方法
 * @param state 整体状态对象，用于存储和管理系统状态
 * @param input 用户输入的字符串内容
 */
  private void setStateInput(OverAllState state, String input) {
    // 使用 input() 方法
    Map<String, Object> inputMap = new HashMap<>();
    inputMap.put("input", input);
    state.input(inputMap);
  }
}